// 自动载入插件
const loadPlugins = require('gulp-load-plugins')
const plugins = loadPlugins()

// 返回当前命令行所在的工作目录
const cwd = process.cwd()
let config = {
  // default
  build: {
    src: 'src',
    dist: 'dist',
    temp: 'temp',
    public: 'public',
    paths: {
      styles: 'assets/styles/*.scss',
      scripts: 'assets/scripts/*.js',
      pages: '*.html',
      images: 'assets/images/**',
      fonts: 'assets/fonts/**'
    }
  }
}
try {
  const loadConfig = require(`${cwd}/pages.config.js`)
  config = Object.assign({}, config, loadConfig)
} catch (error) {}

/* 编译style */
const { src, dest } = require('gulp')
// 会自动安装 node-sass 核心转换模块
// const sass = require('gulp-sass')

const style = () => {
  /**
   * 使用 base 参数指定基准路径，会将 scss 文件转到指定目录下
   * 但是此时每次生成的文件不会被清空，之后会处理
   * cwd 指定路径
   */
  // return src('src/assets/styles/*.js', { base: 'src' })
  return src(config.build.paths.styles, { base: config.build.src, cwd: config.build.src })
    /**
     * 转换 scss，sass 会认为以_开头的文件都是不带下划线的文件所依赖的文件，会忽略掉它
     * 使用 outputStyle: expend，会将 css 文件中括号结尾放在新的一行
     */
    .pipe(plugins.sass({ outputStyle: 'expanded' }))
    // .pipe(dest('dist'))
    .pipe(dest(config.build.temp)) // 由于使用useref会二次转换，因此要把这里编译的文件放入临时目录下
    // watch 中可监视到变化，serve 中就可以不写 files 了，将内部文件的信息以流的方式推到浏览器
    .pipe( bs.reload({ stream: true }))
}

/* 编译 js */
// 使用babel转换，插件只是用来唤醒 @babel-core 转换过程，不会自动安装
// const babel = require('gulp-babel')

const script = () => {
  // return src('src/assets/scripts/*.js', { base: 'src' })
  return src(config.build.paths.scripts, { base: config.build.src, cwd: config.build.src })
    // 使用 preset-env 进行转换
    // .pipe(plugins.babel({ presets: ['@babel/preset-env']}))
    // 使用 require 的方式找
    .pipe(plugins.babel({ presets: [require('@babel/preset-env')]}))
    // .pipe(dest('dist'))
    .pipe(dest(config.build.temp)) // 由于使用useref会二次转换，因此要把这里编译的文件放入临时目录下
    .pipe( bs.reload({ stream: true }))
}

/* 编译模板 */
// const swig = require('gulp-swig')
// 模板中会有一些数据变量，这些数据需要在模板引擎工作时指定
// const { data } = require('./data')

const page = () => {
  // src/**/*.html: src 下任意子目录下的 html 文件
  // return src('src/*.html', { base: 'src' })
  return src(config.build.paths.pages, { base: config.build.src, cwd: config.build.src })
    // cache 设置为 false，防止模板缓存导致页面不能及时更新
    .pipe(plugins.swig({ data: config.data, defaults: { cache: false } }))
    // .pipe(dest('dist'))
    .pipe(dest(config.build.temp)) // 由于使用useref会二次转换，因此要把这里编译的文件放入临时目录下
    .pipe( bs.reload({ stream: true }))
}


/* 转换 图片&字体，进行压缩（国外插件，请注意） */
// const imagemin = require('gulp-imagemin')

const image = () => {
  // return src('src/assets/images/**', { base: 'src' })
  return src(config.build.paths.images, { base: config.build.src, cwd: config.build.src })
    .pipe(plugins.imagemin())
    .pipe(dest(config.build.dist))
}
// 字体文件同样可以使用 imagemin
const font = () => {
  // return src('src/assets/fonts/**', { base: 'src' })
  return src(config.build.paths.fonts, { base: config.build.src, cwd: config.build.src })
    .pipe(plugins.imagemin())
    .pipe(dest(config.build.dist))
}

/* 创建组合任务 - 并行更好 */
const { parallel } = require('gulp')
// const compile = parallel(style, script, page, image, font)
// 由于后文 develop 构建时可减少不常用文件的构建，因此在这里去掉 image & font 组合
const compile = parallel(style, script, page)


/* 其他文件（public）可直接通过拷贝的方式 */
const extra = () => {
  // return src('public/**', { base: 'public' })
  return src('**', { base: config.build.public, cwd: config.build.public })
    .pipe(dest(config.build.dist))
}

/* 创建组合任务 - 将 compile 与 其他文件组合 */
// const build = parallel(compile, extra)


/* 开发服务器 */
// 可热更新
const browserSync = require('browser-sync')
// 会自动创建一个开发服务器
const bs = browserSync.create()

/* watch 监听文件 */
const { watch } = require('gulp')

const serve = () => {
  /**
   * 监听文件变化
   * @param {string} globs 通配符, 监视产生构建任务的路径
   * @param {function} fn 任务函数
   * 
   */
  // watch('src/assets/styles/*.js', style)
  // watch('src/assets/scripts/*.js', script)
  // watch('src/*.html', page)
  watch(config.build.paths.styles, { cwd: config.build.src }, style)
  watch(config.build.paths.scripts, { cwd: config.build.src }, style)
  watch(config.build.paths.pages, { cwd: config.build.src }, style)
  // 由于以下三个不常更新，因此可将它们设置为发布之前再进行构建
  // watch('src/assets/images/**', image),
  // watch('src/assets/fonts/**', font),
  // watch('public/**', extra),

  // 减少构建的次数，同时监视，执行一种操作
  // 文件变化后会构建浏览器，自动更新浏览器，浏览器重新对文件的请求，可拿到最新文件
  watch([
    // 'src/assets/images/**',
    // 'src/assets/fonts/**',
    // 'public/**'
    config.build.paths.images,
    config.build.paths.fonts,
  ], { cwd: config.build.src }, bs.reload)
  watch('**', { cwd: config.build.public }, bs.reload)

  // 初始化web服务器的相关配置
  bs.init({
    notify: false, // 去掉右上角连接成功的提示
    port: 2020, // 设置启动端口
    // open: false, // 取消启动时自动打开浏览器
    // files: 'dist/**', // browser-sync 自动监听的文件
    server: {
      // baseDir: 'dist', // 指定网站根目录
      // baseDir: ['tem'p, 'src', 'public'], // 改为数组，去数组第一个目录中去找，找不到再依次去找，提高开发时的构建效率
      baseDir: [config.build.temp, config.build.src, config.build.public], // 改为数组，去数组第一个目录中去找，找不到再依次去找，提高开发时的构建效率
      // 加一个路由，将node_modules 下的引入都指定到同一个目录下，此配置会优先于baseDir
      routes: {
        '/node_modules': 'node_modules'
      }
    }
  })
}

/**
 * 将 index.html 中引入的文件构建为一个新的文件
 * 因此需要压缩一下
 * */ 
const useref = () => {
  // 使用 dist (改为 temp，因为上文将js、css、html 文件放入了临时目录)，因为 src 下的 html 文件是模板
  // return src('temp/*.html', { base: 'temp' }) // base可省略
  return src(config.build.paths.pages, { base: config.build.temp, cwd: config.build.temp }) // base可省略
    // 类似 css 文件要在 temp (临时文件）下找，在 node_modules 文件要在根目录下找
    .pipe(plugins.useref({ searchPath: [config.build.temp, '.'] }))
    // 压缩 html、css、js
    .pipe(plugins.if(/\.js$/, plugins.uglify()))
    .pipe(plugins.if(/\.css$/, plugins.cleanCss()))
    .pipe(plugins.if(/\.html$/, plugins.htmlmin({
      collapseWhitespace: true, // 折叠所有空白字符
      minifyCSS: true, // 压缩 style 标签中的 css
      minifyJS: true // 压缩 script 标签内部脚本
    })))
    .pipe(dest(config.build.dist)) // 放在新的目录下，防止读写冲突
}

/* 清除文件，删掉 dist & temp 目录 */
// del 返回一个promise
const del = require('del')

const clean = () => {
  return del([config.build.dist, config.build.temp])
}

const { series } = require('gulp')
const develop = series(compile, serve)

// 上线前执行的任务 - 重新包装一下 build，先清除文件，再进行编译，这是个串行任务
// 先编译、再压缩，因此需要将 compile 和 useref 进行串行
const build = series(clean, parallel(series(compile, useref), extra, image, font))

// 临时导出，之后会将其作为组合导出
// 使用组合任务，就不需临时导出 style、script、page了
module.exports = {
  clean,
  build,
  develop
}